/************************************************************************
 * SSViewer - State Space Viewer for SMLib Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * The following component is required for SSViewer: 
 * GLEE.msi (http://research.microsoft.com/research/downloads/Details/f1303e46-965f-401a-87c3-34e1331d32c5/Detailslynn.aspx?0sr=a). 
 * The terms and conditions governing GLEE are contained in the license provided 
 * through the above link apply to your use of GLEE. Any license restrictions 
 * provided in the GLEE license are in addition to, not in lieu of, the terms 
 * and conditions of this license. 
 * IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE.
 * ***********************************************************************
 * File         :     SSViewer\AutomataVisualizer.cs
 * Description  :     Direct call of visualizer
 * Change log   :     
 * To Do        :
 ************************************************************************/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Glee.Drawing;
using SMLib.Base;


namespace Visualization
{
    /// <summary>
    /// The State Space Viewer: Based on "State Space" defined in SMLib. 
    /// <remarks>This class is working base on ".ToString()" method of states and transition, so states should 
    /// have unique state string identifiers.
    /// For automata included by default in SMLib (DFA, MM, IA), it has more features.
    /// </remarks>
    /// </summary>
    public partial class SSViewer<STATETYPE, TRANSITIONTYPE>
        : Form
    {
        public delegate System.Drawing.Color TransitionColorSpecifier(TRANSITIONTYPE tr);
        public delegate System.Drawing.Color StateColorSpecifier(STATETYPE st);

        TransitionColorSpecifier TransitionColorSpecifierHandler;
        StateColorSpecifier StateColorSpecifierHandler;

        object selectedObjectAttr;
        object selectedObject;
		static bool showOrphNodes = true;
		
		public static void ShowOrphantNodes(bool show)
		{
			showOrphNodes=show;
		}

        IDiGraph<STATETYPE, TRANSITIONTYPE> myStatespace;
        ToolTip toolTip1 = new ToolTip();


        System.Drawing.Color TransitionColorSpecifier_Def(TRANSITIONTYPE tr)
        {
            return System.Drawing.Color.Blue;
        }

        System.Drawing.Color StateColorSpecifier_Def(STATETYPE st)
        {
            return System.Drawing.Color.Black;
        }

        /// <summary>
        /// Initialize the form and variables
        /// </summary>
        void init()
        {
            this.Load += new EventHandler(Form1_Load);
            InitializeComponent();

            this.toolTip1.Active = true;
            toolTip1.AutoPopDelay = 5000;
            toolTip1.InitialDelay = 1000;
            toolTip1.ReshowDelay = 500;
            // Force the ToolTip text to       toolTip1.ShowAlways = true;
        }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="myGraph">the graph to be drawn</param>
        /// <param name="stateColorFunc">specify the color of states</param>
        /// <param name="transColorFunc">specify the color of transitions</param>
        public SSViewer(IDiGraph<STATETYPE, TRANSITIONTYPE> myGraph,
            StateColorSpecifier stateColorFunc = null,
            TransitionColorSpecifier transColorFunc = null)
        {
            init();
            myStatespace = myGraph;
            if (stateColorFunc == null)
                StateColorSpecifierHandler = StateColorSpecifier_Def;
            else
                StateColorSpecifierHandler = stateColorFunc;

            if (transColorFunc == null)
                TransitionColorSpecifierHandler = TransitionColorSpecifier_Def;
            else
                TransitionColorSpecifierHandler = transColorFunc;
        }

        void Form1_Load(object sender, EventArgs e)
        {
            gViewer.SelectionChanged += new EventHandler(gViewer_SelectionChanged);
            gViewer.MouseClick += new MouseEventHandler(gViewer_MouseClick);
        }

        void gViewer_MouseClick(object sender, MouseEventArgs e)
        {
            //if (e.Button == System.Windows.Forms.MouseButtons.Right)
            //    MessageBox.Show("right button");
        }

        void gViewer_SelectionChanged(object sender, EventArgs e)
        {
            if (selectedObject != null)
            {
                if (selectedObject is Edge)
                    (selectedObject as Edge).Attr = selectedObjectAttr as EdgeAttr;
                else if (selectedObject is Node)
                    (selectedObject as Node).Attr = selectedObjectAttr as NodeAttr;

                //selectedObject = null;
                //this.propertyGrid1.SelectedObject = selectedObject;
            }

            if (gViewer.SelectedObject == null)
            {
                label1.Text = "No object under the mouse";
                this.gViewer.SetToolTip(toolTip1, "");
            }
            else
            {
                selectedObject = gViewer.SelectedObject;

                if (selectedObject is Edge)
                {

                    selectedObjectAttr = (gViewer.SelectedObject as Edge).Attr.Clone();
                    (gViewer.SelectedObject as Edge).Attr.Color = Microsoft.Glee.Drawing.Color.Magenta;
                    (gViewer.SelectedObject as Edge).Attr.Fontcolor = Microsoft.Glee.Drawing.Color.Magenta;
                    Edge edge = gViewer.SelectedObject as Edge;

                    //here you can use e.Attr.Id or e.UserData to get back to you data
                    this.gViewer.SetToolTip(this.toolTip1, String.Format("edge from {0} {1}", edge.Source, edge.Target));

                }
                else if (selectedObject is Node)
                {
                    //Show the content of selected state in "property grid"
                    int nodeID = 0;
                    try
                    {
                        nodeID = int.Parse((selectedObject as Node).Id);

                        if (myStatespace[nodeID - 1] != null || myStatespace[nodeID - 1] is IContainedObject)
                            this.myPropertyGrid.SelectedObject = ((IContainedObject)myStatespace[nodeID - 1]).Content;
                    }
                    catch
                    {
                        //nodeID = 0;
                        //foreach (STATETYPE s in myStatespace.GetStates())
                        //{
                        //    if (s.Identifier.Equals((selectedObject as Node).Id))
                        //        break;
                        //    nodeID++;
                        //}
                    }



                    selectedObjectAttr = (gViewer.SelectedObject as Node).Attr.Clone();
                    (selectedObject as Node).Attr.Color = Microsoft.Glee.Drawing.Color.Magenta;
                    (selectedObject as Node).Attr.Fontcolor = Microsoft.Glee.Drawing.Color.Magenta;

                    //here you can use e.Attr.Id to get back to your data
                    this.gViewer.SetToolTip(toolTip1, String.Format("node {0}", (selectedObject as Node).Attr.Label));
                }
                label1.Text = selectedObject.ToString();
            }
            gViewer.Invalidate();
        }


		private void button1_Click(object sender, EventArgs e)
        {
            
            if (myStatespace == null)
            {
                MessageBox.Show("Null state space!");
                return;
            }
            else if (myStatespace.Size == 0)
            {
                MessageBox.Show("Empty!");
                return;
            }

            Microsoft.Glee.Drawing.Graph myGraph = new Microsoft.Glee.Drawing.Graph("graph");
            myGraph.GraphAttr.NodeAttr.Padding = 3;
            
            //myGraph.GraphAttr.LayerDirection = LayerDirection.LR;
			

            for (int i = 0; i < myStatespace.Size; i++)
            {
                STATETYPE s = myStatespace[i];

				if(s==null)
				{
					Console.WriteLine("["+i+"]th state of graph is null!");
					continue;
				}
				
				//* if show orphant nodes, we create nodes before
				if(showOrphNodes && myGraph.FindNode(s.ToString())==null)
					myGraph.AddNode(s.ToString());
				
                foreach (TRANSITIONTYPE t in this.myStatespace.TransitionsOf(s))
                {
                    Edge edge = (Edge)myGraph.AddEdge(s.ToString(), myStatespace.DestinationOf(t).ToString());
				
					if(edge==null)
					{
						Console.WriteLine("Error: destination is null!");
						continue;
					}
                    edge.Attr.Label = t.ToString();
                    System.Drawing.Color myTrColor = TransitionColorSpecifierHandler(t);
                    edge.Attr.Color = new Color(myTrColor.R, myTrColor.G, myTrColor.B);
					
					if(t is  SMLib.Automata.IA.InterfaceAutomaton.IA_Step)
					{
						if((t as SMLib.Automata.IA.InterfaceAutomaton.IA_Step).StepType==
						   SMLib.Automata.IA.InterfaceAutomaton.IA_Step.Step_Type.EVENT)
						{
							//edge.Attr.AddStyle(Microsoft.Glee.Drawing.Style.Dashed);
							//edge.Attr.Color = Microsoft.Glee.Drawing.Color.Orange;
						}
					}
					
                    //SSColoeSpec.ColorToGleeColor(colorSpec.TransitionColorSpecifier(t));
                    //Microsoft.Glee.Drawing.Color.Black;
                    //if (t.isControllable)
                    //{
                    //    edge.Attr.Color = Microsoft.Glee.Drawing.Color.Black;
                    //}
                    //else
                    //{
                    //    //edge.Attr.Color = Microsoft.Glee.Drawing.Color.Orange;
                    //    edge.Attr.AddStyle(Microsoft.Glee.Drawing.Style.Dashed);
                    //}
                }
            }

			
            for (int i = 0; i < myStatespace.Size; i++)
            {
				if(myStatespace[i]==null)
				{
					Console.WriteLine("["+i+"]th state of graph is null!");
					continue;
				}
                System.Drawing.Color myStColor = StateColorSpecifierHandler(myStatespace[i]);
                Node currentNode = (myGraph.FindNode(myStatespace[i].ToString()) as Node);
				
				if(currentNode==null)
				{
					//Console.WriteLine("["+i+"]th state of graph is null!");
					continue;
				}
				currentNode.Attr.Color = new Color(myStColor.R, myStColor.G, myStColor.B);
				
				//* in case if it is DFA!
				if(myStatespace[i] is SMLib.Automata.DFA.DFA.DFA_State)
				{	
					SMLib.Automata.DFA.DFA.DFA_State mystate= 
						(myStatespace[i] as SMLib.Automata.DFA.DFA.DFA_State);
					if(mystate.isAccepting)
					{
						currentNode.Attr.Fillcolor=Color.LightBlue;
					}
				}
            }

			//* set initial node as double circle
            (myGraph.FindNode(myStatespace[0].ToString()) as Node).Attr.Shape = Shape.DoubleCircle;

            gViewer.Graph = myGraph;

        }

//        private static void CreateSourceNode(Node a)
//        {
//            a.Attr.Shape = Microsoft.Glee.Drawing.Shape.Box;
//            a.Attr.XRad = 3;
//            a.Attr.YRad = 3;
//            a.Attr.Fillcolor = Microsoft.Glee.Drawing.Color.Green;
//            a.Attr.LineWidth = 10;
//        }
//
//        private void CreateTargetNode(Node a)
//        {
//            a.Attr.Shape = Microsoft.Glee.Drawing.Shape.DoubleCircle;
//            a.Attr.Fillcolor = Microsoft.Glee.Drawing.Color.LightGray;
//            a.Attr.LabelMargin = -4;
//        }


        private void recalculateLayoutButton_Click(object sender, EventArgs e)
        {
            this.gViewer.Graph = this.myPropertyGrid.SelectedObject as Microsoft.Glee.Drawing.Graph;
        }

    }
}
